﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace GenericLibrary.Common {

    public interface IReferenceable<TReference> where TReference : Reference<TReference>, new() {

        void OnReferenced();
        void OnDereferenced();
    }

    public abstract class Reference<T> where T : Reference<T>, new() {

        private static Dictionary<IReferenceable<T>, WeakReference> _references = new Dictionary<IReferenceable<T>, WeakReference>();

        private IReferenceable<T> _target;

        protected IReferenceable<T> Target {
            get {
                return this._target;
            }
        }

        ~Reference() {
            try {
                lock( _references ) {
                    _references.Remove( this._target );
                    this._target.OnDereferenced();
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public static T Create( IReferenceable<T> target ) {
            try {
                lock( _references ) {
                    if( _references.ContainsKey( target ) ) {
                        return _references[ target ].Target as T;
                    }
                    T reference = new T();
                    reference._target = target;
                    _references[ target ] = new WeakReference( reference );
                    target.OnReferenced();
                    return reference;
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public static bool IsReferenced( IReferenceable<T> target ) {
            try {
                lock( _references ) {
                    return _references.ContainsKey( target );
                }
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }
    }

    public static class ReferenceExtension {

        public static TReference GetReference<TReference>( this IReferenceable<TReference> target ) where TReference : Reference<TReference>, new() {
            try {
                return Reference<TReference>.Create( target );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }

        public static bool IsReferenced<TReference>( this IReferenceable<TReference> target ) where TReference : Reference<TReference>, new() {
            try {
                return Reference<TReference>.IsReferenced( target );
            } catch( Exception ) {
                Debug.Assert( false );
                throw;
            }
        }


    }
}
